use apikit::ApiError;
use axum::{extract::FromRequestParts, http::request::Parts, response::IntoResponse};
use heck::ToSnakeCase;
use serde::de::DeserializeOwned;
use serde_json::{Map, Value};
use thiserror::Error;
use validator::SelfValidator;

use crate::{
    error::{BadRequest, EntityError, MapEntityError},
    param::Header,
};

use super::error_to_response;

#[derive(Debug, Error, ApiError)]
pub enum HeaderRejection {
    #[error(transparent)]
    Serde(#[from] EntityError),

    #[error(transparent)]
    Validate(#[from] BadRequest),
}

impl IntoResponse for HeaderRejection {
    fn into_response(self) -> axum::response::Response {
        error_to_response(self)
    }
}

impl<T, S> FromRequestParts<S> for Header<T>
where
    T: DeserializeOwned + SelfValidator,
    S: Send + Sync,
{
    type Rejection = HeaderRejection;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let headers = &parts.headers;
        let mut map: Map<String, Value> = Map::new();
        for header in headers {
            let name = header.0.as_str().to_string().to_snake_case();
            let value = header.1.to_str().unwrap_or_default();
            map.insert(name, Value::from(value));
        }
        let value = Value::Object(map);
        let data = T::deserialize(value).map_entity_error()?;
        data.validate(None, None)
            .map_err(|e| BadRequest::from(e.to_string()))?;
        Ok(Self(data))
    }
}
